﻿/*
 * Authors:    Kęstutis Džiautas
 *             Edmundas Matusevičius
 * University: VU, MIF, SE, 4 course
 * Date:       2010-11-20
 * Version:    1.0
 */

using System;
using System.Collections.Generic;
using System.IO;

namespace SyntaxAnalizer
{
    /*
     * Use constructor Graph once. It will create the graph from gremmar file.
     * Use defDictionary to see all defenitions and to start as a starting point of graph. First node is <Decals>
     * Recursivnes: use isRecursive() to check if recursive. isEnd() and isStart() use for determine if start or end of recursion.
     * 
     */
    public class Graph
    {
        public Graph()
        {
            if (File.Exists(filepath))
            {
                file = null;
            }
            defDictionary = new Dictionary<string, GraphNode>();
            readGrapth();
        }

        /* Start reading graph */
        private void readGrapth()
        {
            try
            {
                file = new StreamReader(filepath);
                while ((line = file.ReadLine()) != null)
                {
                    if (line.Length != 0)
                    {
                        if ((line[0] != '!') && (line[0] != ' ') && (line[0] != '\t'))
                        {
                            parseLine(line);
                        }

                    }
                }
                foreach (GraphNode gn in defDictionary.Values)
                {
                    father = gn;
                    checkDef(gn);
                }
            }
            finally
            {
                if (file != null)
                {
                    file.Close();
                }
            }
        }
        private void parseLine(string line)
        {
            string def = "";
            string[] alter;

            alter = split(line, ref def);
            defDictionary.Add(def, createNode(alter));
        }
        private GraphNode createNode(string[] alter)
        {
            GraphNode node = new GraphNode();
            string alt = alter[0],
                   str;

            node.Def = takeNext(ref alt);

            str = takeNext(ref alt);
            //Next
            if (str != "")
            {
                node.Next = createNext(str, alt);
            }
            alter = removeFirst(alter);
            //Alter
            if (alter != null)
            {
                node.AltDef = createNode(alter);
            }
            return node;
        }
        private GraphNode createNext(string def, string alter)
        {
            GraphNode node = new GraphNode();
            string alt;

            node.Def = def;
            alt = takeNext(ref alter);
            if (alt != "")
            {
                node.Next = createNext(alt, alter);
            }
            return node;
        }
        private string[] removeFirst(string[] alter)
        {
            if (alter.Length == 1)
            {
                return null;
            }
            else
            {
                string[] alter2 = new string[alter.Length - 1];
                for (int i = 1; i < alter.Length; i++)
                {
                    alter2[i - 1] = alter[i];
                }
                return alter2;
            }
        }
        private string takeNext(ref string alter)
        {
            string str = "";
            int place = 0;

            if (alter != "")
            {
                switch (alter[0])
                {

                    case '\'':
                        {
                            //HardCoded sorry
                            if (alter[1] == '\'')
                            {
                                alter = alter.Remove(0, 3);
                                return "'''";
                            }
                            place = alter.IndexOf("'", 1);
                            str = alter.Substring(0, place + 1);
                            alter = alter.Remove(0, place + 1);
                            alter = alter.Trim();
                        }
                        break;
                    case '<':
                        {
                            place = alter.IndexOf(">", 1);
                            str = alter.Substring(0, place + 1);
                            alter = alter.Remove(0, place + 1);
                            alter = alter.Trim();
                        }
                        break;
                    case '#':
                        {
                            str = "#";
                            alter = alter.Remove(0, 1);
                            alter = alter.Trim();
                        }
                        break;
                }
            }
            else
            {
                return "";
            }

            return str;
        }
        private string[] split(string str, ref string def)
        {
            string alter;
            string[] alt;

            str = str.Trim(' ', '\t');
            def = (str.Substring(0, str.IndexOf(">") + 1)).Trim(' ', '\t');
            alter = (str.Substring(str.IndexOf(">"), str.Length - str.IndexOf(">"))).TrimStart('>', '\t').Trim(' ', '\t').Remove(0, 3);
            alter.Trim(' ', '\t');


            //alt = alter.Split('|');
            alt = splitAlter(alter);

            for (int i = 0; i < alt.Length; i++)
            {
                alt[i] = alt[i].Trim();
            }

            return alt;
        }

        private string[] splitAlter(string alter)
        {

            int index = 0, count = 0;
            string[] str = new string[100];

            while (index < alter.Length)
            {

                if (alter[index] == '<')
                {
                    index = alter.IndexOf('>', index);
                }
                else if (alter[index] == '\'')
                {
                    if (alter[index + 1] != '\'')
                    {
                        index = alter.IndexOf('\'', index + 1);
                    }
                    else
                    {
                        index += 2;
                    }
                }
                else if (alter[index] == '|')
                {
                    str[count] = alter.Substring(0, index);
                    alter = alter.Remove(0, index + 1);
                    count++;
                    index = -1;
                }

                index++;
            }
            str[count++] = alter;

            string[] str1 = new string[count];
            int i = 0;

            foreach (string s in str)
            {
                if (s != null)
                {
                    str1[i] = s;
                    i++;
                }
                else { break; }
            }
            return str1;

        }
        private void checkDef(GraphNode node)
        {
            if (defDictionary.ContainsKey(node.Def))
            {
                node.NextDef = defDictionary[node.Def];
                if (node.NextDef == father)
                {
                    node.End = true;
                    node.Recursive = true;
                    father.Start = true;
                    father.Recursive = true;
                }
            }
            if (node.Next != null)
            {
                checkDef(node.Next);
            }
            if (node.AltDef != null)
            {
                checkDef(node.AltDef);
            }
        }

        /// <summary>
        /// Returns root node declaration.
        /// </summary>
        /// <returns>Root node declaration.</returns>
        public string getRootNodeDeclaration() {
            return "<Start>";
        }

        /* List of all Definitions */
        public Dictionary<string, GraphNode> defDictionary;

        private GraphNode father;
        StreamReader file;
        string line;
        string filepath = @"mini-C formated.grm";
    }
}
